﻿using System;
using System.Collections.Generic;
using System.Linq;
using ScrumPilot.Domain.Models;
using ScrumPilot.Helpers;
using ScrumPilot.TFSHelpers;

namespace ScrumPilot.Domain.BPL
{
    public class KanbanDashBoardServices : DashBoardServices
    {
        public KanbanDashBoardServices(TFSServices tfs) : base(tfs) { }
        public KanbanDashBoardServices(TFSServices tfs, String tfsRootUrl) : base(tfs, tfsRootUrl) { }

        /// <summary>
        /// Get the Key Perf Indicator on Bugs
        /// </summary>
        /// <returns>a KPIBugs object</returns>
        public KPIBugs GetKPIBusgs()
        {
            this.GetAllBugItems();
            var kp = new KeyDataIndicators
                         {
                             BugsCount = this.BugItemsList.Count(),
                             CriticalItemsCount = (this.WorkItemsList == null ? 0 :
                                this.WorkItemsList.Count(
                                p => p.GetWorkItemType() == WorkItemType.Task && p.IsActive() && p.GetPriority() == 1)) + // Count critical tasks
                                (this.BugItemsList == null ? 0 : this.BugItemsList
                                .Count(p => p.IsActive() && p.GetPriority() == 1))
                         };

            var kpi = new KPIBugs
                          {
                              kpi = this.SetKeyindicatorCSS(kp),
                              BugItems = this.HistoryBugs()
                          };
            return kpi;
        }

        /// <summary>
        /// Compute the graph bugs
        /// </summary>
        /// <returns>a KPIBugItem object</returns>
        public KPIBugItem HistoryBugs()
        {
            var dateList = DateTimeHelpers.GetDateWorkDays(DateTime.UtcNow, Range);
            var qry = new TFSQuery(TFS.TfsWorkItemStore);
            var bugsList = qry.GetCompletedWorkItemList(dateList[0]).OrderByDescending(p => p.GetResolvedDate()).ToArray();
            var bugsListc = qry.GetWorkItemListByCreated(dateList[0], new List<string> { "Bug" }).ToArray();
            var kpiRet = new KPIBugItem
                             {
                                 FixedBugs =
                                     dateList.Select(
                                         date =>
                                         bugsList.Count(p => p.GetResolvedDate() != null && p.GetResolvedDate().Value.Date == date.Date))
                                             .ToArray(),
                                 NewBugs =
                                     dateList.Select(
                                         date =>
                                         bugsListc.Count(p => p.CreatedDate.Date == date.Date))
                                             .ToArray()
                             };

            return kpiRet;
        }

        public KPIKanbanProjects KPIProjects()
        {
            var dateList = DateTimeHelpers.GetDateWorkDays(DateTime.UtcNow, Range);
            var qry = new TFSQuery(TFS.TfsWorkItemStore);

            var tasksList = qry.GetCompletedWorkItemList(dateList[0], 1);
            var usList = qry.GetCompletedWorkItemList(dateList[0], 3);
            var oRet = new KPIKanbanProjects
                           {
                               Totals = new TotalItems
                                            {
                                                TotalDone = tasksList.Count(),
                                                TotalHoursCompleted = (int)Decimal.Round(tasksList.Sum(p => p.GetCompletedWork())),
                                                TotalUsDone = usList.Count()
                                            },
                               HistoryDone = dateList.Select(dateTime => tasksList.Count(p => p.GetDateTaskCompleted() != null &&
                                 p.GetDateTaskCompleted().Value.Date == dateTime)).ToArray(),
                               HistoryHoursCompleted = dateList.Select(dateTime => tasksList.Where(p => p.GetDateTaskCompleted() != null &&
                                   p.GetDateTaskCompleted().Value.Date == dateTime).Sum(s => s.GetCompletedWork())).ToArray(),
                               HistoryUsDone = dateList.Select(dateTime => usList.Count(p => p.GetDateUsCompleted() != null &&
                                   p.GetDateUsCompleted().Value.Date == dateTime)).ToArray()
                           };
            oRet.Totals.AverageUsDone = oRet.Totals.TotalUsDone / Range;
            oRet.Totals.AverageHoursCompleted = oRet.Totals.TotalHoursCompleted / Range;
            oRet.Totals.AverageDone = oRet.Totals.TotalDone / Range;

            return oRet;
        }

        public List<WorkItemSimpleForUser> GetWorkItems(string iteration, byte state)
        {
            try
            {
                var myState = WorkItemState.Todo;
                var type = "User Story";
                switch (state)
                {
                    case 0: // todo
                        myState = WorkItemState.Todo;
                        break;
                    case 1: // process
                        myState = WorkItemState.InProgress;
                        break;
                    case 2: // test
                        myState = WorkItemState.ToTest;
                        break;
                    case 3: // done
                        myState = WorkItemState.Done;
                        break;
                    case 4: // bug total
                        type = "Bug";
                        break;
                    case 5: // bug process
                        myState = WorkItemState.InProgress;
                        type = "Bug";
                        break;
                }
                var tfsq = new TFSQuery(TFS.TfsWorkItemStore);
                var wil = tfsq.GetWorkItemList(iteration, new List<WorkItemState> { myState }, new List<String> { type }, false);
                if (type == "User Story")
                {
                    if (myState == WorkItemState.InProgress)
                    {
                        wil = wil.Where(p => !p.IsUserStoryReadyToTest()).ToList();
                    }
                    else if (myState == WorkItemState.ToTest)
                    {
                        wil = wil.Where(p => p.IsUserStoryReadyToTest()).ToList();
                    }
                }
                return new List<WorkItemSimpleForUser>(this.CopyAndMapFromTFS(wil));
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Get the kanban projects list
        /// </summary>
        /// <returns></returns>
        public KanbanMainTable KanbanProjects()
        {
            var iterations = this.GetKanbanIteration();

            this.GetKanbanWorkItems(iterations);
            var uswitl = this.WorkItemsList.Where(p => p.GetWorkItemType() == WorkItemType.UserStory).ToArray();
            var bugwitl = this.WorkItemsList.Where(p => p.GetWorkItemType() == WorkItemType.Defect).ToArray();

            foreach (var iteration in iterations)
            {
                var myUsItems = uswitl.Where(p => p.IterationId == iteration.SID).ToArray();
                var myBugItems = bugwitl.Where(p => p.IterationId == iteration.SID).ToArray();
                iteration.UserStories = new List<int>
                                       {
                                           myUsItems.Count(p => p.GetState() == WorkItemState.Todo),
                                           myUsItems.Count(
                                               p =>
                                               p.GetState() == WorkItemState.InProgress && !p.IsUserStoryReadyToTest()),
                                           myUsItems.Count(p => p.IsUserStoryReadyToTest()),
                                           myUsItems.Count(p => p.GetState() == WorkItemState.Done),
                                           myBugItems.Count(p=> p.GetState() == WorkItemState.InProgress),
                                           myBugItems.Count(p=> p.GetState() == WorkItemState.Todo)
                                        };

                iteration.WorkItemClasses = new List<int>
                                          {
                                              (int) iteration.Type
                                          };

            }

            // Get params from DB for kanban limit
            // Todo : get this data from TFS kanban def (by project)
            var kanbanmaxdoing = this.ScrumPilotParam.kanbanmaxdoing;
            var kanbanmaxtotest = this.ScrumPilotParam.kanbanmaxtotest;
            // prepare color status for view
            var sumRun = iterations.Sum(p => p.UserStories[1]);
            var maxRunColor = sumRun > kanbanmaxdoing ? 1 : sumRun < kanbanmaxdoing ? 2 : 0;
            var sumTest = iterations.Sum(p => p.UserStories[2]);
            var maxTestColor = sumTest > kanbanmaxtotest ? 1 : sumTest < kanbanmaxtotest ? 2 : 0;
            // set color status for each iteration
            foreach (var iteration in iterations)
            {
                iteration.WorkItemClasses.Add(maxRunColor);
                iteration.WorkItemClasses.Add(maxTestColor);
                var toLate = iteration.FinishDate == null ? 0 : DateTime.Parse(iteration.FinishDate) < DateTime.Today ? 1 : 0;

                iteration.WorkItemClasses.Add(toLate);
            }

            return new KanbanMainTable
            {
                Iterations = iterations,
                KanbanIpMax = this.ScrumPilotParam.kanbanmaxdoing ?? -1,
                KanbanTtMax = this.ScrumPilotParam.kanbanmaxtotest ?? -1
            };

        }

        private KanbanIteration[] GetKanbanIteration()
        {
            List<KanbanIteration> lRet;
            using (var unitOfWork = new UnitOfWork())
            {
                // Iterations selected by admin
                var iterations = unitOfWork.KanbanIterationRepository.GetAll();
                lRet = iterations.Select(ite => new KanbanIteration
                {
                    ProjectID = ite.project.projectid,
                    ProjectName = ite.project.name,
                    ID = ite.iterationpath,
                    SID = ite.iterationid,
                    Type = KanbanIterationType.Standard
                }).ToList();

            }

            // Add iteration from bugs 
            // could be different from slected list
            // if yes, we add those projects/iterations
            this.GetAllBugItems();
            var existId = lRet.Select(p => p.SID).ToArray();
            foreach (var item in BugItemsList.Where(p => !existId.Contains(p.IterationId)).ToArray())
            {
                if (lRet.Count(p => p.SID == item.IterationId) == 0)
                    lRet.Add(new KanbanIteration
                    {
                        ProjectID = item.Project.Id,
                        ProjectName = item.Project.Name,
                        ID = item.IterationPath,
                        SID = item.IterationId,
                        Type = KanbanIterationType.Bug
                    });
            }

            // Scan TFS to know if there is no active US which his project/iteration
            // is not in the selected list
            var qry = new TFSQuery(TFS.TfsWorkItemStore);
            var extraItems = qry.GetWorkItemList(new Dictionary<string, string>(), new List<string> { "User Story" },
                                                new List<WorkItemState>
                                                    {
                                                        WorkItemState.InProgress,
                                                        WorkItemState.Reopen
                                                    }, false, false);

            existId = lRet.Select(p => p.SID).ToArray();
            // Which project/iteration is not in the list
            extraItems = extraItems.Where(p => !existId.Contains(p.IterationId)).Where(p => p.GetWorkItemType() == WorkItemType.UserStory).ToList();
            foreach (var item in extraItems)
            {
                if (lRet.Count(p => p.SID == item.IterationId) == 0)
                    lRet.Add(new KanbanIteration
                    {
                        ProjectID = item.Project.Id,
                        ProjectName = item.Project.Name,
                        ID = item.IterationPath,
                        SID = item.IterationId,
                        Type = KanbanIterationType.Extra
                    });
            }

            // Set the schedule
            foreach (var iteration in lRet)
            {
                var sched = TFS.GetIterationSchedule(iteration.ID);
                if (sched != null && sched.FinishDate != null)
                {
                    iteration.FinishDate = sched.FinishDate.Value.ToString("o");
                }
            }

            return lRet.OrderBy(o => o.ProjectName).ThenByDescending(o => o.ID).ToArray();
        }

        private void GetKanbanWorkItems(IEnumerable<KanbanIteration> iterations)
        {
            if (TFS != null && TFS.IsAuthenticated && TFS.TfsWorkItemStore != null)
            {
                TFS.RefreshCache();
                var qry = new TFSQuery(TFS.TfsWorkItemStore);
                // Get all workitems using in dashboard
                var myItems = iterations.Select(iteration => new Tuple<string, string>(iteration.ProjectName, iteration.ID)).ToArray();
                this.WorkItemsList = qry.GetWorkItemList(myItems, null, null, false, false);
            }
        }

        public class KPIKanbanProjects
        {
            public TotalItems Totals { get; set; }
            public int[] HistoryDone { get; set; }
            public decimal[] HistoryHoursCompleted { get; set; }
            public int[] HistoryUsDone { get; set; }
        }

        public class TotalItems
        {
            public int TotalDone { get; set; }
            public int TotalHoursCompleted { get; set; }
            public int TotalUsDone { get; set; }
            public int AverageDone { get; set; }
            public int AverageHoursCompleted { get; set; }
            public int AverageUsDone { get; set; }
        }

        public class KPIBugs
        {
            // ReSharper disable InconsistentNaming
            public KeyDataIndicators kpi { get; set; }
            // ReSharper restore InconsistentNaming
            public KPIBugItem BugItems { get; set; }
        }

        public class KPIBugItem
        {
            public int[] NewBugs { get; set; }
            public int[] FixedBugs { get; set; }
        }

        public class KanbanIteration
        {
            public int ProjectID { get; set; }
            public String ProjectName { get; set; }
            public String ID { get; set; }
            public int SID { get; set; }
            public String Uri { get; set; }
            public String FinishDate { get; set; }
            public KanbanIterationType Type { get; set; }
            public List<int> WorkItemClasses { get; set; }
            public List<int> UserStories { get; set; }
        }

        public class KanbanMainTable
        {
            public int KanbanIpMax { get; set; }
            public int KanbanTtMax { get; set; }
            public KanbanIteration[] Iterations { get; set; }
        }

        public enum KanbanIterationType
        {
            Standard = 0, Bug = 1, Extra = 2
        }
    }
}
